package contegor.scire.structure.queries;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import contegor.scire.core.Concept;
import contegor.scire.core.Container;
import contegor.scire.core.InfiniteSingletonList;
import contegor.scire.core.Link;
import contegor.scire.core.NeedsPrecedent;
import contegor.scire.core.ReturnPoint;
import contegor.scire.core.ReturnPointAdapter;
import contegor.scire.core.Task;
import contegor.scire.core.TaskId;
import contegor.scire.core.Version;
import contegor.scire.core.WithConsequences;
import contegor.scire.core.queries.GetLinksFromSource;
import contegor.scire.core.queries.Query;

/**
 * {@link CircularQuery} that, with a {@link Link}s set given by its {@link NeedsPrecedent} interface,
 * gets the {@link Link}s of their target {@link Concept}s.
 *
 * @version $Revision$
 * @author  Héctor Cirbián Pousa ($Author$)
 */
@Version("$Id$")
public class CircularGetLinksUpstream extends CircularQuery<Link> implements WithConsequences<Link, Link> {
  private static final long serialVersionUID = -8609002380600485841L;

  private List<? extends Link> precedentResults = null;
  private List<Query<Link>>    consequences     = null;
  private List<Link>           results          = Collections.synchronizedList(new ArrayList<Link>());

  private transient ReturnPoint<Link> consequencesReturnPoint = null;

  @Override
  public void setPrecedent(Task<? extends Link> task, Collection<? extends Link> results) {
    this.precedentResults = new ArrayList<Link>(results);
  }

  @Override
  public void process(Container targetContainer) {
    if (this.precedentResults != null) {
      this.consequences = new ArrayList<Query<Link>>(1);
      for (Link link: this.precedentResults) {
        if (link.isVisibleTo(this.taskId.getTransactionId())) {
          GetLinksFromSource consequence = new GetLinksFromSource(link.getTarget());
          consequence.setTaskId(taskId.next(true));
          this.consequences.add(consequence);
        }
      }
    }
  }

  @Override
  public List<? extends Task<Link>> getConsequences() {
    return this.consequences;
  }

  @Override
  public List<? extends ReturnPoint<? super Link>> getConsequencesReturnPoints() {
    if ((this.consequences == null) || (this.consequences.size() == 0))
      return Collections.emptyList();
    if (this.consequencesReturnPoint == null)
      this.consequencesReturnPoint = generateConsequencesReturnPoint();
    return new InfiniteSingletonList<ReturnPoint<? super Link>>(this.consequencesReturnPoint, consequences.size());
  }

  @Override
  public List<? extends Collection<Link>> getResultsForSubsequentTasks() {
    if ((this.subsequents == null) || (this.subsequents.size() == 0))
      return Collections.emptyList();
    return new InfiniteSingletonList<List<Link>>(this.results, this.subsequents.size());
  }

  @Override
  public List<? extends ReturnPoint<? super Link>> getSubsequentTasksReturnPoints() {
    if ((this.subsequents == null) || (this.subsequents.size() == 0))
      return Collections.emptyList();
    return new InfiniteSingletonList<ReturnPoint<? super Link>>(this.returnPoint, subsequents.size());
  }

  @Override
  public CircularGetLinksUpstream withIt(NeedsPrecedent<? super Link, Link> subsequent) {
    super.withIt(subsequent);
    return this;
  }

  private ReturnPoint<Link> generateConsequencesReturnPoint() {
    return new ReturnPointAdapter<Link>() {
      public void manage(TaskId taskId, Link result) {
        results.add(result);
      }
      public void manage(TaskId taskId, Exception exception) {
        returnPoint.manage(taskId, exception);
      }
    };
  }
}
